package com.eian.boot.crypto.impl.asymmetric;

import com.eian.boot.crypto.constant.CryptoConstants;
import com.eian.boot.crypto.core.EncryptStrategy;
import com.eian.boot.crypto.enums.CryptoAlgorithm;
import com.eian.boot.crypto.exception.CryptoException;
import com.eian.boot.crypto.model.CryptoContext;
import com.eian.boot.crypto.model.OutputFormat;
import com.eian.boot.crypto.utils.Base64Utils;
import com.eian.boot.crypto.utils.HexUtils;
import com.eian.boot.crypto.utils.KeyUtils;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;

/**
 * SM2 加密策略（国密）
 * <p>
 * 使用公钥加密
 *
 * @author eian
 */
public class SM2EncryptStrategy implements EncryptStrategy {

    @Override
    public CryptoAlgorithm algorithm() {
        return CryptoAlgorithm.SM2;
    }

    @Override
    public String encrypt(String plaintext, CryptoContext context) {
        if (StringUtils.isEmpty(plaintext)) {
            return plaintext;
        }
        byte[] encrypted = encrypt(plaintext.getBytes(StandardCharsets.UTF_8), context);
        return context.getOutputFormat() == OutputFormat.HEX
                ? HexUtils.toHex(encrypted)
                : Base64Utils.encode(encrypted);
    }

    @Override
    public byte[] encrypt(byte[] plaintext, CryptoContext context) {
        if (plaintext == null || plaintext.length == 0) {
            return plaintext;
        }

        try {
            // 获取公钥
            if (StringUtils.isEmpty(context.getPublicKey())) {
                throw new CryptoException("SM2 加密时必须提供公钥");
            }
            PublicKey publicKey = KeyUtils.toPublicKey(context.getPublicKey(), CryptoConstants.Algorithm.SM2);

            // 执行加密（使用 BouncyCastle 提供者）
            Cipher cipher = Cipher.getInstance(algorithm().getTransformation(), CryptoConstants.Provider.BOUNCY_CASTLE);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(plaintext);
        } catch (Exception e) {
            throw new CryptoException("SM2 加密失败", e);
        }
    }
}

